﻿using System;
using System.Collections.Generic;
using System.Text;
using Graffiti.Core;
using System.Xml;
using System.IO;
using System.Collections.Specialized;
using System.Web;
using GraffitiExtras.Utility;
using System.Text.RegularExpressions;

namespace GraffitiExtras.Plugins.Sitemap
{
    public class Sitemap : GraffitiEvent
    {
        public string SITEMAPPATH;
        public string SITEDOMAIN;

        #region EditableForm members

        public override string Name
        {
            get
            {
                return "Sitemap";
            }
        }

        public override string Description
        {
            get
            {
                return "Sitemap generator for Graffiti. By <a href=\"http://nayyeri.net\">Keyvan Nayyeri</a>.";
            }
        }

        public override bool IsEditable
        {
            get
            {
                return true;
            }
        }

        protected override NameValueCollection DataAsNameValueCollection()
        {
            NameValueCollection nvc = base.DataAsNameValueCollection();
            nvc["SITEMAPPATH"] = SITEMAPPATH;
            nvc["SITEDOMAIN"] = SITEDOMAIN;

            return nvc;
        }

        public override StatusType SetValues(HttpContext context, NameValueCollection nvc)
        {
            base.SetValues(context, nvc);
            SITEMAPPATH = nvc["SITEMAPPATH"];
            SITEDOMAIN = nvc["SITEDOMAIN"];
            return StatusType.Success;
        }

        protected override FormElementCollection AddFormElements()
        {
            FormElementCollection fec = new FormElementCollection();
            fec.Add(new TextFormElement("SITEMAPPATH", "Sitemap Path", "Sitemap path (i.e. '~/sitemap.xml')"));
            fec.Add(new TextFormElement("SITEDOMAIN", "Domain Name", "Domain name (i.e. google.com or nayyeri.net)"));
            return fec;
        }

        #endregion

        #region GraffitiEvent members

        public override void Init(GraffitiApplication ga)
        {
            ga.AfterCommit += new DataObjectEventHandler(ga_AfterCommit);
        }

        void ga_AfterCommit(DataBuddyBase dataObject, EventArgs e)
        {
            Post post = dataObject as Post;
            if (post != null)
                GenerateSitemap();

            AddSiteMapToRobots();
        }

        #endregion

        #region Private members

        private void GenerateSitemap()
        {
            string xml = GetXml();

            HttpContext context = HttpContext.Current;

            try
            {
                Graffiti.Core.Util.CreateFile
                    (context.Server.MapPath(this.SITEMAPPATH), xml);
            }
            catch (Exception ex)
            {
                Log.Error("Error in Google Sitemap plug-in", ex.ToString());
            }
        }

        private string GetXml()
        {
            StringBuilder sb = new StringBuilder();

            StringWriterWithEncoding stringWriter =
                new StringWriterWithEncoding(sb, Encoding.UTF8);

            XmlWriter xmlWriter = XmlWriter.Create(stringWriter);

            // Start document
            xmlWriter.WriteStartDocument();
            // <urlset>
            xmlWriter.WriteStartElement("urlset",
                "http://www.sitemaps.org/schemas/sitemap/0.9");

            // Homepage
            // <url>
            xmlWriter.WriteStartElement("url");
            // <loc />
            xmlWriter.WriteElementString("loc",
                new Macros().FullUrl(new Urls().Home));
            // <lastmod />
            xmlWriter.WriteElementString("lastmod",
                DateTime.Now.ToString("yyyy-MM-dd"));
            // <changefreq />
            xmlWriter.WriteElementString("changefreq", "daily");
            // <priority />
            xmlWriter.WriteElementString("priority", "0.5");
            // </url>
            xmlWriter.WriteEndElement();

            // Categories
            CategoryCollection categories = CategoryCollection.FetchAll();
            foreach (Category category in categories)
            {
                if (!category.IsUncategorized)
                {
                    // <url>
                    xmlWriter.WriteStartElement("url");
                    // <loc />
                    xmlWriter.WriteElementString("loc",
                        new Macros().FullUrl(category.Url));
                    // <lastmod />
                    xmlWriter.WriteElementString("lastmod",
                        DateTime.Now.ToString("yyyy-MM-dd"));
                    // <changefreq />
                    xmlWriter.WriteElementString("changefreq", "daily");
                    // <priority />
                    xmlWriter.WriteElementString("priority", "0.5");
                    // </url>
                    xmlWriter.WriteEndElement();
                }
            }

            // Posts
            PostCollection posts = PostCollection.FetchAll();
            posts.Sort(delegate(Post post1, Post post2)
            {
                return Comparer<DateTime>.Default.Compare(post2.Published, post1.Published);
            });

            foreach (Post post in posts)
            {
                if ((post.IsPublished) && (!post.IsDeleted) && (!post.IsDirty))
                {
                    if (post.Published <= DateTime.Now)
                    {
                        // <url>
                        xmlWriter.WriteStartElement("url");
                        // <loc />
                        string url = new Macros().FullUrl(post.Url);
                        if (!string.IsNullOrEmpty(SITEDOMAIN))
                            url = string.Format("http://{0}{1}", SITEDOMAIN, post.Url);
                        xmlWriter.WriteElementString("loc", url);
                        // <lastmod />
                        xmlWriter.WriteElementString("lastmod",
                            post.ModifiedOn.ToString("yyyy-MM-dd"));
                        // <changefreq />
                        xmlWriter.WriteElementString("changefreq", "daily");
                        // <priority />
                        xmlWriter.WriteElementString("priority", "0.5");
                        // </url>
                        xmlWriter.WriteEndElement();
                    }
                }
            }

            // </urlset>
            xmlWriter.WriteEndElement();

            xmlWriter.Flush();
            xmlWriter.Close();
            stringWriter.Flush();
            return stringWriter.ToString();
        }

        private void AddSiteMapToRobots()
        {
            try
            {
                HttpContext context = HttpContext.Current;

                string url = new Macros().FullUrl(VirtualPathUtility.ToAbsolute(SITEMAPPATH));
                string pattern = string.Format("Sitemap: {0}", url);

                string filePath = context.Server.MapPath("~/robots.txt");

                string robotsTxt = File.ReadAllText(filePath);

                if (!Regex.IsMatch(robotsTxt, pattern))
                {
                    File.AppendAllText(filePath, (Environment.NewLine + Environment.NewLine + pattern));
                }
            }
            catch (Exception ex)
            {
                Log.Error("Error in Google Sitemap plug-in", ex.ToString());
            }
        }

        #endregion
    }
}
